﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.UI.WebControls;
using System.Xml.Linq;
using Medianamik.Core;
using Medianamik.Core.Sugar;
using Medianamik.Globalization;
using Medianamik.UI.Controls;
using SubSonic;
using Telerik.Web.UI;
using CodeService=Medianamik.Core.CodeGeneration.CodeService;

namespace Medianamik.UI.TypeManagement
{
    //TODO: Validation des champs

    public partial class TypeManagement : BaseTypePage
    {
        private Guid? _baseTypeID; //Permet l'ajout d'un type sous un type parent
        private Guid? _typeID;

        protected void Page_Init(object sender, EventArgs e)
        {
            var validateInterfaceWindow = new BaseModalWindow { ID = "validateInterfaceWindow" };

            if (!Page.IsPostBack)
            {
                validateInterfaceWindow.IconUrl = "/Medianamik/images/Icones/Ico_Valider.gif";
                validateInterfaceWindow.ReloadOnShow = true;
                validateInterfaceWindow.Width = Unit.Pixel(400);
                validateInterfaceWindow.Height = Unit.Pixel(200);
            }

            MyWindowManager.Windows.Add(validateInterfaceWindow);
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            //Les params ne sont pas obligatoires et c'est un ou l'autre

            string strTypeID = Request.QueryString[DefaultParamNames.TypeID];

            if (!String.IsNullOrEmpty(strTypeID)) //Édition d'un type
            {
                if (!StringHelper.IsGuid(strTypeID, ref _typeID) || CurrentType == null)
                {
                    Response.Redirect(ReturnURL);
                }
            }
            else //Ajout - Le BaseType est optionnel (ajout à la racine)
            {
                string strBaseTypeID = Request.QueryString["basetypeid"];

                if (!String.IsNullOrEmpty(strBaseTypeID) && (!StringHelper.IsGuid(strBaseTypeID, ref _baseTypeID)
                    || BaseType == null))
                {
                    Response.Redirect(ReturnURL);
                }
            }


            if (!IsPostBack)
            {
                InterfaceChoice.NodeTypeId = _typeID;
                phrTemplates.Visible = IsBaseWebType;
                LoadTypes();
                LoadInterfaces();

                if (IsEdition)
                {
                    SetValues();
                    LoadNodes();
                }
            }
        }

        protected void ValidateInterfaceChoice(object source, ServerValidateEventArgs args)
        {
            args.IsValid = true;
            var selectedInterfaceIds = InterfaceChoice.SelectedInterfaceIds;

            if (selectedInterfaceIds.Count() > 0)
            {
                var currenType = CurrentType ?? BaseType;

                if (currenType == null || currenType.GetAllProperties().Count == 0)
                {
                    args.IsValid = false;
                    InterfaceChoiceValidator.ErrorMessage =
                        "Ce type ne peut pas implémenter une interface car il possède aucune propriété.";
                }
                else
                {
                    var invalidInterfaces = currenType.RespectsInterfaces(InterfaceChoice.SelectedInterfaceIds);
                    var numberOfInvalidInterfaces = invalidInterfaces.Count();

                    args.IsValid = numberOfInvalidInterfaces <= 0;

                    if (numberOfInvalidInterfaces > 0)
                        InterfaceChoiceValidator.ErrorMessage = "Ce type ne respecte pas les interfaces suivantes : " +
                                                                invalidInterfaces.ToString(", ");
                }
            }
        }

        protected void LegitimateParentTypeSelected(object sender, RadTreeNodeEventArgs e)
        {
            if (e.Node.Checked)
            {
                //Ajouter
                AddParentNodes(e.Node);
            }
            else
            {
                //Retirer
                var node = legitimateParentNodeTree.Nodes.FindNodeByValue(e.Node.Value);
                if (node != null)
                    legitimateParentNodeTree.Nodes.Remove(node);
            }
            legitimateParentNodeTree.Nodes.Sort();
        }

        protected void LegitimateParentNodeTreeNodeExpanded(object sender, RadTreeNodeEventArgs e)
        {
            ExpandNode(e.Node);
        }

        protected void ExpandNode(RadTreeNode node)
        {
            if (node == null) return;

            var childNodes = NodeManager.GetNodesByType(new Guid(node.Value), true);

            childNodes.ForEach(n => node.Nodes
                .Add(new RadTreeNode(n.Name, n.ID.ToString())
                   {
                       ExpandMode = TreeNodeExpandMode.ClientSide,
                       Checkable = true
                   }));

            node.Expanded = true;
            node.ExpandMode = TreeNodeExpandMode.ClientSide;
        }

        protected RadTreeNode AddParentNodes(RadTreeNode pTypeNode)
        {
            var type = new NodeType(new Guid(pTypeNode.Value));
            if (type.GetNodeCount() > 0)
            {
                var newNode = new RadTreeNode(pTypeNode.Text, pTypeNode.Value)
                {
                    ExpandMode = TreeNodeExpandMode.ServerSide,
                    Checkable = false
                };
                legitimateParentNodeTree.Nodes.Add(newNode);
                return newNode;
            }
            return null;
        }

        protected void btnSave_Click(object sender, EventArgs e)
        {
            if (IsValid)
            {
                //Vérifier que le nom du type est unique

                var legitimateParentTypes = new List<NodeType>();

                foreach (RadTreeNode item in legitimateParentTypeTree.CheckedNodes)
                {
                    legitimateParentTypes.Add(new NodeType(new Guid(item.Value)));
                }

                var legitimateParentNodes = new List<Node>();
                legitimateParentNodes.AddRange(NodeManager.GetNodes(legitimateParentNodeTree.CheckedNodes.Select(n => new Guid(n.Value)).ToArray()));

                var templates = new List<TypeTemplate>();
                templates.AddRange(templatePicker.Templates.AsEnumerable());

                if (IsEdition)
                {
                    CurrentType.Name = txtTypeName.Text;
                    CurrentType.Description = txtTypeDescription.Text;
                    CurrentType.IsInheritable = chkCanHaveChilds.Checked;
                    CurrentType.IsGenerated = chkMayBeGenerated.Checked;

                    SetGenerationProperties();

                    CurrentType.IsAbstract = chkIsAbstract.Checked;
                    CurrentType.IsSystem = chkIsSystem.Checked;
                    CurrentType.LegitimateParentTypes.Clear();
                    CurrentType.LegitimateParentTypes.AddRange(legitimateParentTypes);
                    CurrentType.LegitimateParentNodes.Clear();
                    CurrentType.LegitimateParentNodes.AddRange(legitimateParentNodes);

                    CurrentType.Templates.Clear();
                    CurrentType.Templates.AddRange(templates);

                    CurrentType.Indexable = chkIndexable.Checked;
                    CurrentType.Listable = chkListable.Checked;
                    var isVersionedChanged = CurrentType.IsVersioned != chkIsVersioned.Checked;
                    CurrentType.IsVersioned = chkIsVersioned.Checked;
                    if (isVersionedChanged)
                    {
                        //On a coché/décoché "Est Versionable"
                        //Il faut regénérer la vue liée au Type et les vues de tous les types parents
                        var code = new CodeService();
                        var types = NodeTypeManager.GetParentTypes(CurrentType.ID, true);
                        foreach (var t in types)
                        {
                            code.GenerateView(t);
                        }
                        //Et supprimer le vieux Data si on passe le type en NonVersionable
                        if (!chkIsVersioned.Checked)
                        {
                            code.SwitchToNonVersionableTypeMaintenance(CurrentType);
                        }
                    }

                    CurrentType.IconPath = IconPicker.Value;
                }
                else
                {
                    _currentType = new NodeType(txtTypeName.Text, chkCanHaveChilds.Checked,
                        chkMayBeGenerated.Checked, chkIsAbstract.Checked, chkIsSystem.Checked,
                        _baseTypeID, false /* IsManyToMany */, legitimateParentTypes,
                        legitimateParentNodes, templates, (BaseType == null ? null : BaseType.ExtendedProperties))
                        {
                            Indexable = chkIndexable.Checked,
                            Listable = chkListable.Checked,
                            Description = txtTypeDescription.Text,
                            IconPath = IconPicker.Value,
                            IsVersioned = chkIsVersioned.Checked
                        };
                }

                var toDelete = CurrentType.Interfaces
                    .Where(o => !InterfaceChoice
                                     .SelectedInterfaceIds.Any(i => o.InterfaceId.Equals(i))).ToList();

                foreach (var interf in toDelete)
                    CurrentType.Interfaces.Remove(interf);

                foreach (var interfaceId in InterfaceChoice.SelectedInterfaceIds)
                {
                    if (!CurrentType.Interfaces.Any(i => i.InterfaceId.Equals(interfaceId)))
                        CurrentType.Interfaces.Add(new NodeTypeInterfaceImplementation(CurrentType.ID, interfaceId));
                }

                //TODO: Catcher les différentes exceptions Médianamik et afficher les messages correspondant
                NodeTypeManager.SaveType(CurrentType);

                Translator.CreateResource("TypeManagement", CurrentType.ID + "_TypeName",
                    TypeNameTranslations.Values);

                Response.Redirect(ReturnURL);
            }
        }

        private void GetGenerationProperties()
        {
            cbCreate.Checked =
                CurrentType.ExtendedProperties.GenerationPlatforms.Elements().Any(
                    elm => elm.Name == PersistenceMethods.Create);

            cbRead.Checked =
                CurrentType.ExtendedProperties.GenerationPlatforms.Elements().Any(
                    elm => elm.Name == PersistenceMethods.Read);

            cbUpdate.Checked =
                CurrentType.ExtendedProperties.GenerationPlatforms.Elements().Any(
                    elm => elm.Name == PersistenceMethods.Update);

            cbDelete.Checked =
                CurrentType.ExtendedProperties.GenerationPlatforms.Elements().Any(
                    elm => elm.Name == PersistenceMethods.Delete);

            cbCSharp.Checked =
                CurrentType.ExtendedProperties.GenerationPlatforms.Elements().Any(
                    elm => (string)elm.Attribute("Name") == PlatformNames.CSharp);

            cbCSharpWP7.Checked =
                CurrentType.ExtendedProperties.GenerationPlatforms.Elements().Any(
                    elm => (string)elm.Attribute("Name") == PlatformNames.CSharpWp7);

            cbJavaAndroid.Checked =
                CurrentType.ExtendedProperties.GenerationPlatforms.Elements().Any(
                    elm => (string)elm.Attribute("Name") == PlatformNames.JavaAndroid);

            cbObjectiveC.Checked =
                CurrentType.ExtendedProperties.GenerationPlatforms.Elements().Any(
                    elm => (string)elm.Attribute("Name") == PlatformNames.ObjectiveC);
        }

        private void SetGenerationProperties()
        {
            var xelem = CurrentType.ExtendedProperties.GenerationPlatforms.Elements().FirstOrDefault(
                elm => elm.Name == PersistenceMethods.Create);

            if (cbCreate.Checked && xelem == null)
            {
                CurrentType.ExtendedProperties.GenerationPlatforms.Add(new XElement(PersistenceMethods.Create));
            }
            else if (!cbCreate.Checked && xelem != null)
            {
                xelem.Remove();
            }

            xelem = CurrentType.ExtendedProperties.GenerationPlatforms.Elements().FirstOrDefault(
                elm => elm.Name == PersistenceMethods.Read);

            if (cbRead.Checked && xelem == null)
            {
                CurrentType.ExtendedProperties.GenerationPlatforms.Add(new XElement(PersistenceMethods.Read));
            }
            else if (!cbRead.Checked && xelem != null)
            {
                xelem.Remove();
            }

            xelem = CurrentType.ExtendedProperties.GenerationPlatforms.Elements().FirstOrDefault(
                elm => elm.Name == PersistenceMethods.Update);

            if (cbUpdate.Checked && xelem == null)
            {
                CurrentType.ExtendedProperties.GenerationPlatforms.Add(new XElement(PersistenceMethods.Update));
            }
            else if (!cbUpdate.Checked && xelem != null)
            {
                xelem.Remove();
            }

            xelem = CurrentType.ExtendedProperties.GenerationPlatforms.Elements().FirstOrDefault(
                elm => elm.Name == PersistenceMethods.Delete);

            if (cbDelete.Checked && xelem == null)
            {
                CurrentType.ExtendedProperties.GenerationPlatforms.Add(new XElement(PersistenceMethods.Delete));
            }
            else if (!cbDelete.Checked && xelem != null)
            {
                xelem.Remove();
            }
            
            xelem = CurrentType.ExtendedProperties.GenerationPlatforms.Elements().FirstOrDefault(
                elm => (string)elm.Attribute("Name") == PlatformNames.ObjectiveC);

            if (cbObjectiveC.Checked && xelem == null)
            {
                CurrentType.ExtendedProperties.GenerationPlatforms.Add(new XElement("Platform", new XAttribute("Name", PlatformNames.ObjectiveC)));
            }
            else if (!cbObjectiveC.Checked && xelem != null)
            {
                xelem.Remove();
            }

            xelem = CurrentType.ExtendedProperties.GenerationPlatforms.Elements().FirstOrDefault(
                elm => (string)elm.Attribute("Name") == PlatformNames.CSharp);

            if (cbCSharp.Checked && xelem == null)
            {
                CurrentType.ExtendedProperties.GenerationPlatforms.Add(new XElement("Platform", new XAttribute("Name", PlatformNames.CSharp)));
            }
            else if (!cbCSharp.Checked && xelem != null)
            {
                xelem.Remove();
            }

            xelem = CurrentType.ExtendedProperties.GenerationPlatforms.Elements().FirstOrDefault(
                elm => (string)elm.Attribute("Name") == PlatformNames.CSharpWp7);

            if (cbCSharpWP7.Checked && xelem == null)
            {
                CurrentType.ExtendedProperties.GenerationPlatforms.Add(new XElement("Platform", new XAttribute("Name", PlatformNames.CSharpWp7)));
            }
            else if (!cbCSharpWP7.Checked && xelem != null)
            {
                xelem.Remove();
            }

            xelem = CurrentType.ExtendedProperties.GenerationPlatforms.Elements().FirstOrDefault(
                elm => (string)elm.Attribute("Name") == PlatformNames.JavaAndroid);

            if (cbJavaAndroid.Checked && xelem == null)
            {
                CurrentType.ExtendedProperties.GenerationPlatforms.Add(new XElement("Platform", new XAttribute("Name", PlatformNames.JavaAndroid)));
            }
            else if (!cbJavaAndroid.Checked && xelem != null)
            {
                xelem.Remove();
            }
          
        }

        protected void TypeNameUnicityValidator_ServerValidate(object sender, ServerValidateEventArgs e)
        {
            e.IsValid = !((IsEdition && CurrentType.Name != txtTypeName.Text &&
                          NodeTypeManager.NodeTypeExists(txtTypeName.Text))
                          || (!IsEdition && NodeTypeManager.NodeTypeExists(txtTypeName.Text)));
        }
        protected void TypeNameValidator_ServerValidate(object sender, ServerValidateEventArgs e)
        {
            var validator = new NodeTypeNameValidator(new CSharpCodeLanguage());
            e.IsValid = validator.Validate(txtTypeName.Text);
        }

        private NodeType _currentType;
        public NodeType CurrentType
        {
            get
            {
                if (_currentType == null && _typeID.HasValue)
                {
                    _currentType = NodeTypeManager.GetNodeType(_typeID.Value);
                }

                return _currentType;
            }
        }

        private NodeType _baseType;
        public NodeType BaseType
        {
            get
            {
                if (_baseType == null && _baseTypeID.HasValue)
                {
                    _baseType = NodeTypeManager.GetNodeType(_baseTypeID.Value);
                }

                return _baseType;
            }
        }

        private string _returnURL;
        public string ReturnURL
        {
            get
            {
                if (_returnURL == null)
                {
                    if (CameFromSearchPage)
                    {
                        _returnURL = SearchURL;
                    }
                    else
                    {
                        _returnURL = "TypeList.aspx";
                        if (_baseTypeID.HasValue)
                            _returnURL += "?basetypeid=" + _baseTypeID;
                        else if (CurrentType != null && CurrentType.ParentID.HasValue)
                        {
                            _returnURL += "?basetypeid=" + CurrentType.ParentID;
                        }
                    }
                }

                return _returnURL;
            }
        }

        private bool IsEdition
        {
            get
            {
                return _typeID.HasValue;
            }
        }

        private bool? _isBaseWebType;
        private bool IsBaseWebType
        {
            get
            {
                if (_isBaseWebType == null)
                {
                    _isBaseWebType = CurrentType == null
                                   ? BaseType != null && BaseType.Inherits(MedianamikTypes.BaseWebType)
                                   : CurrentType.Inherits(MedianamikTypes.BaseWebType);
                }
                return _isBaseWebType.Value;
            }
        }

        private bool ModuleIsSelected
        {
            get
            {
                RadTreeNode item = legitimateParentTypeTree.Nodes.FindNodeByValue(MedianamikTypes.Module.ToString());

                return (item != null && item.Checked);
            }
        }

        private void SetValues()
        {
            txtTypeName.Text = CurrentType.Name;
            txtTypeDescription.Text = CurrentType.Description;
            chkCanHaveChilds.Checked = CurrentType.IsInheritable;
            chkIsSystem.Checked = CurrentType.IsSystem;
            chkIsAbstract.Checked = CurrentType.IsAbstract;
            chkIsAbstract.Enabled = !CurrentType.HasNodes;

            cbCSharp.Enabled = CurrentType.IsGenerated;
            cbJavaAndroid.Enabled = CurrentType.IsGenerated;
            cbCSharpWP7.Enabled = CurrentType.IsGenerated;
            cbObjectiveC.Enabled = CurrentType.IsGenerated;
            cbCreate.Enabled = CurrentType.IsGenerated;
            cbRead.Enabled = CurrentType.IsGenerated;
            cbUpdate.Enabled = CurrentType.IsGenerated;
            cbDelete.Enabled = CurrentType.IsGenerated;

            //if (!ExtendedProperties.PropertyPositions.Elements().Any(elm => (Guid)elm.Attribute("Id") == guid))

            GetGenerationProperties();

            //Désactiver le checkbox Inheritable si le type a déjà des enfants
            if (chkCanHaveChilds.Checked && CurrentType.HasChildren)
                chkCanHaveChilds.Enabled = false;

            chkMayBeGenerated.Checked = CurrentType.IsGenerated;
            chkIndexable.Checked = CurrentType.Indexable;
            chkListable.Checked = CurrentType.Listable;
            chkIsVersioned.Checked = CurrentType.IsVersioned && !chkListable.Checked;
            chkIsVersioned.Enabled = !chkListable.Checked;


            foreach (var parent in CurrentType.ReadOnlyOriginalLegitimateParentTypes)
            {
                var currentCheckBox = legitimateParentTypeTree.FindNodeByValue(parent.ID.ToString());

                if (currentCheckBox != null)
                {
                    currentCheckBox.Expanded = true;
                    currentCheckBox.ExpandParentNodes();
                    currentCheckBox.Checked = true;
                }
            }
            //TODO : Sélectionnner les noeuds


            InterfaceChoice.SelectedInterfaceIds = CurrentType.Interfaces.Select(i => i.InterfaceId);


            //Template
            if (IsBaseWebType)
            {
                templatePicker.Templates = CurrentType.Templates.ToArray();
                //templatePicker.SelectedTemplates = CurrentType.ClientTemplatePaths;
            }

            //Icone
            IconPicker.Value = CurrentType.IconPath;

            //Traductions
            if (CurrentType.Status != EntityStates.New)
            {
                TypeNameTranslations.Values = Translator.GetResources("TypeManagement",
                    CurrentType.ID + "_TypeName",
                    ConfigManager.GetMedianamikCultures().Select(c => c.CultureInfo));
            }

        }

        private void LoadTypes()
        {
            legitimateParentTypeTree.DataTextField = "Name";
            legitimateParentTypeTree.DataValueField = "ID";
            legitimateParentTypeTree.DataFieldID = "ID";
            legitimateParentTypeTree.DataFieldParentID = "ParentID";
            legitimateParentTypeTree.DataSource = NodeTypeManager.GetAllTypes(false).ToHierarchicalListWithArtificialRoot();
            legitimateParentTypeTree.DataBind();
        }

        private void LoadInterfaces()
        {
            InterfaceChoice.DataSource = NodeTypeManager.GetAllNodeTypeInterfaces();
            InterfaceChoice.DataBind();
        }

        private void LoadNodes()
        {
            legitimateParentNodeTree.Nodes.Clear();
            foreach (var node in legitimateParentTypeTree.CheckedNodes)
            {
                var newNode = AddParentNodes(node);
                ExpandNode(newNode);
            }
            SetNodeValues();
        }

        private void SetNodeValues()
        {
            foreach (var legitimateNode in CurrentType.LegitimateParentNodes)
            {
                var node = legitimateParentNodeTree.FindNodeByValue(legitimateNode.ID.ToString());

                if (node != null)
                {
                    node.Checked = true;
                    node.Expanded = true;
                    node.ExpandParentNodes();
                }
            }
        }
    }
}